﻿@using Microsoft.FluentUI.AspNetCore.Components
@using System.Text.Json
@using System.Text.RegularExpressions
@using System.Web
@using eShopSupport.ServiceDefaults.Clients.Backend
@inject IJSRuntime JS

@if (!State.Message.IsAssistant)
{
    <div class="message">
        <div class="sender-icon">
            <FluentPersona Image="@anonymousPersonImage" ImageSize="30px" />
        </div>
        <div class="sender-name">You</div>
        <div class="message-content">
            <div class="message-text">@State.Message.Text</div>
        </div>
    </div>
}
else
{
    <div class="message assistant @(visible ? "visible" : "")">
        <div class="sender-icon">
            <FluentPersona Image="@assistantImage" ImageSize="30px" />
        </div>
        <div class="sender-name">Assistant</div>
        <div class="message-content" @ref="messageContentElement">
            @if (!string.IsNullOrEmpty(searchText))
            {
                <div class="search-info">@searchText</div>
            }

            <div class="message-text">@(string.IsNullOrEmpty(State.Message.Text) ? "..." : State.Message.Text)</div>

            @if (isSuggestedReply)
            {
                <div class="use-as-reply">
                    <FluentButton Appearance="Appearance.Accent" OnClick="@UseAsReplyAsync">Use as reply</FluentButton>
                </div>
            }

            @if (!string.IsNullOrEmpty(citationUrl))
            {
                <a href="@citationUrl" class="reference-link" target="_blank">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25" />
                    </svg>
                    <span class="ref-text">@(string.IsNullOrWhiteSpace(citationQuote) ? "Reference" : citationQuote)</span>
                </a>
            }
        </div>
    </div>
}

@code {
    string anonymousPersonImage = "";
    string assistantImage = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='white' class='w-6 h-6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18' /%3E%3C/svg%3E%0A";

    private string? searchText;
    private bool isSuggestedReply;
    private ElementReference messageContentElement;
    private bool visible;
    private string? citationUrl;
    private string? citationQuote;

    [Parameter, EditorRequired]
    public TicketAssistant.MessageState State { get; set; } = default!;

    [Parameter]
    public EventCallback<TicketAssistant.MessageState> OnCompleted { get; set; }

    [Parameter]
    public EventCallback<string> OnSuggestedReply { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender)
        {
            return;
        }

        // Load the module, or just get a reference if it's already loaded
        await using var module = await JS.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Ticket/TicketAssistantMessage.razor.js");

        if (State.ResponseItems is not null)
        {
            var searchResults = new List<AssistantChatReplyItem>();
            try
            {
                // Don't become visible until either a response starts arriving, or some timeout elapses
                // This feels more natural than showing a placeholder reply instantly after you submit a message
                await Task.Delay(500);
                visible = true;
                StateHasChanged();

                await foreach (var item in State.ResponseItems)
                {
                    switch (item.Type)
                    {
                        case AssistantChatReplyItemType.Search:
                            searchText = item.Text;
                            StateHasChanged();
                            break;
                        case AssistantChatReplyItemType.SearchResult:
                            searchResults.Add(item);
                            break;
                        case AssistantChatReplyItemType.AnswerChunk:
                            State.Message.Text += item.Text;
                            await module.InvokeVoidAsync("addAnswerChunk", messageContentElement, item.Text);
                            break;
                        case AssistantChatReplyItemType.IsAddressedToCustomer:
                            isSuggestedReply = item.Text == "true";
                            StateHasChanged();
                            break;
                    }
                }
            }
            catch (TaskCanceledException)
            {
                // Not an error
            }

            // Check if there's a citation to show
            var citeMatches = Regex.Matches(State.Message.Text, "<cite searchResultId=['\"]?(\\d+)['\"]?>(.*?)</cite>");
            if (citeMatches.FirstOrDefault() is { } match
                && int.TryParse(match.Groups[1].Value, out var searchResultId)
                && searchResults.FirstOrDefault(s => s.SearchResultId == searchResultId) is { } searchResult)
            {
                citationQuote = match.Groups[2].Value;
                citationQuote = citationQuote.Trim('.', ',', ' ', '\n', '\r', '\t', '"', '\'');
                citationUrl = $"manual.html?file={searchResult.SearchResultProductId}.pdf&page={searchResult.SearchResultPageNumber}&search={HttpUtility.UrlEncode(citationQuote)}";
                State.Message.Text = State.Message.Text[0..match.Index] + State.Message.Text[(match.Index + match.Length)..];
            }

            State.Message.Text = State.Message.Text.Trim();
            await OnCompleted.InvokeAsync(State);
        }
    }

    private Task UseAsReplyAsync()
        => OnSuggestedReply.InvokeAsync(State.Message.Text);
}
